package org.sergiy.metawidget.rules;

import java.net.URL;
import java.io.File;

import org.drools.rule.Rule;
import org.drools.RuleBase;
import org.drools.WorkingMemory;

import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.AfterClass;
import org.testng.annotations.AfterSuite;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeSuite;
import org.testng.annotations.Test;

/**
 * Test of DRL compilation and running based on drools
 *   
 * @author Sergiy Litsenko
 *
 */
@Test
public class BusinessRulesTestCase extends BusinessRulesCoreTestCase {
	protected static final String TEST_RULES_SRC = "test-rules.drl";
	protected static final String RULES_PKG_NAME = "ui";
	protected static final String DEFAULT_RULE = "Default";
	protected static final String TEST_BUSINESS_RULE_OUTCOME = "TestRule 3";
	protected static final int TEST_BUSINESS_RULE_WEIGTH = 3;
	
	/**
	 * Return DRL sources
	 * 
	 * @return DRL sources
	 */
	@Override
	protected Object[] getDrlSources() {
		URL rulesSrc = this.getClass().getClassLoader().getResource(TEST_RULES_SRC);
		Assert.assertNotNull(rulesSrc, "Unable to get URL for DRL sources");
		// return
		return new Object[] {TEST_RULES_SRC};
	}

	/**
	 * Return array of facts to evaluate (called upon setUp())
	 * @return array of facts to evaluate
	 */
	@Override
	protected Object[] getFacts () {
		return new Object[]{};
	}
	
	@BeforeClass
	@Override
	protected void init() throws Exception {
		super.init();
	}

	@AfterClass
	@Override
	protected void destroy() throws Exception {
		super.destroy();
	}

	@BeforeMethod
	@Override
	protected void setUp() throws Exception {
		super.setUp();
	}

	@AfterMethod
	@Override
	protected void tearDown() throws Exception {
		super.tearDown();
	}
	
	/**
	 * Executes rules located in single DRL file and applies them to the list of facts 
	 * @param drlFile the source file 
	 * @param agendaGroup the agenda group to focus
	 * @param facts the facts 
	 * @throws Exception
	 */
	protected void executeTestBusinessRules(String drlFile, String agendaGroup, DomainObject... facts) throws Exception {
	
		Assert.assertNotNull (facts, "Fact list must exists");
		Assert.assertTrue (facts.length > 0, "Facts must be defined");
		// check only first object
		DomainObject bo = facts[0];
		Assert.assertFalse (bo.isOutcomeSet(), "The initial outcome of the first BusinessClass object must NOT be set");
		// store initial values before firing rules
		String initialOutCome = bo.getOutcome();
		int initialOutComeWeight = bo.getOutcomeWeight();
		//
		// create and execute business rules without retraction
		//
		// create rule base
		RuleBase rb = PackageFactory.createRuleBase(new Object[]{drlFile});
		// check that rule base actually created
		Assert.assertNotNull (rb, "Rule Base shall be created");
		// execute rules
		WorkingMemory wm = PackageFactory.executeRules (rb, agendaGroup, false, (Object[])facts);
		
		Assert.assertNotNull (wm, "A working memory shall be created by Package Factory");
		Assert.assertTrue (bo.isOutcomeSet(), "The outcome of the first BusinessClass object must be set after execution of business rules");
		Assert.assertNotSame(bo.getOutcome(), initialOutCome, "The outcome of BusinessClass object must be changed");
		// for both 'TestRule 3' and 'TestRule 3 - Agenda' 
		Assert.assertTrue (bo.getOutcome().startsWith(TEST_BUSINESS_RULE_OUTCOME), 
				"The outcome of the first BusinessClass object must be set after execution of business rules");
		Assert.assertNotSame(bo.getOutcomeWeight(), initialOutComeWeight, 
				"The outcome weigth of BusinessClass object must be changed");
	}

	@Test
	public void testResourceCompiled() throws Exception {
		// start marker
		logStartTestMarker ("testResourceCompiled");		
		// debug
		if (log.isDebugEnabled()) {
			log.debug(String.format("Checking that the '%1$s' package is compiled.", TEST_RULES_SRC));
		}
		// check that rule base actually created
		Assert.assertNotNull (ruleBase, "Rule Base shall be created");
		org.drools.rule.Package pkg = ruleBase.getPackage (RULES_PKG_NAME);
		Assert.assertNotNull (pkg, String.format("Rule Base shall contain '%1$s' package", RULES_PKG_NAME));
		
		Rule rule = pkg.getRule(DEFAULT_RULE);
		Assert.assertNotNull (rule, String.format("The '%1$s' package shall have the '%2$s' rule", RULES_PKG_NAME, DEFAULT_RULE));
		// debug
		if (log.isDebugEnabled()) {
			log.debug(String.format("The '%1$s' package compiled properly.", TEST_RULES_SRC));
		}
	}

	
	@Test
	public void testExecuteTestRulesInDefaultAgenda() throws Exception {
		// start marker
		logStartTestMarker ("testExecuteTestRulesInDefaultAgenda");
		
		// debug
		if (log.isDebugEnabled()) {
			log.debug(String.format("Testing the execution queue priority (using salience) for the '%1$s' rules...", TEST_RULES_SRC));
		}
		// test business rules with no agenda
		executeTestBusinessRules (TEST_RULES_SRC, null, new DomainObject()); 
		// debug
		if (log.isDebugEnabled()) {
			log.debug(String.format("The test of execution queue priority (using salience) for the '%1$s' rules has been completed.", TEST_RULES_SRC));
		}
	}

}
